/**
 * @class {Root} 常驻根管理器
 * @author DuskyHuang 圣子
 * @description 这是一个跨场景的根节点，可以在编辑器直接可视化调配常见参数。
*/

import {
    _decorator, screen, director, profiler, Enum, Camera,
    Canvas, view, AudioClip, AudioSource, CCString,
} from 'cc';
import { EDITOR } from 'cc/env';
import Mediator from '../core/mvc/Mediator';
import { singleton } from '../utils/decorators';
import { group, serialize } from "../utils";
import NetManager, { Domain } from './net-manager';
import AudioManager from './audio-manager';
import ResManager from './res-manager';
const { ccclass, disallowMultiple, requireComponent } = _decorator;
const net = NetManager.instance;
const aud = AudioManager.instance;
const res = ResManager.instance;
const NetKeys: Keys<NetManager & Root>[]
    = ['useCover', 'simulateLatency', 'latency', 'logReq', 'logResp', 'domain', 'useReport'];
const AudKeys: Keys<AudioManager & Root>[]
    = ['bgmVolume', 'bgmLoop', 'bgmPlayOnAwake', 'sfxVolume', 'sfxButton', 'sfxToggle'];
function showLatency(this: Root) { return this.simulateLatency; }
function showDefaultSfx(this: Root) { return this.useDefaultSfxs; }

@ccclass('cfw.Root')
@disallowMultiple(true)
@requireComponent(Canvas)
@singleton
export default class Root extends Mediator {

    public bgm: any

    declare public static readonly instance: Root;

    /** ----------------- UI ------------------- **/

    @serialize private _fullScreen: boolean = false;
    @group("UI", "启用全屏显示")
    public get fullScreen(): boolean { return this._fullScreen; }
    public set fullScreen(arg: boolean) {
        !EDITOR && (!!arg ? screen.requestFullScreen() : screen.exitFullScreen());
        this._fullScreen = arg;
    }

    @serialize private _showStats: boolean = false;
    @group("UI", "显示性能面板")
    public get showStats(): boolean { return this._showStats; }
    public set showStats(arg: boolean) {
        !EDITOR && !!profiler && (!!arg ? profiler.showStats() : profiler.hideStats());
        this._showStats = arg;
    }

    /** ----------------- Net ------------------- **/

    @serialize private _useCover: boolean = false;
    @group("Net", "使用网络遮罩")
    public get useCover(): boolean { return this._useCover; }
    public set useCover(arg: boolean) { this._useCover = arg; if (!EDITOR) net.useCover = arg; }

    @serialize private _simulateLatency: boolean = false;
    @group("Net", "启用模拟延迟")
    public get simulateLatency(): boolean { return this._simulateLatency; }
    public set simulateLatency(arg: boolean) { this._simulateLatency = arg; if (!EDITOR) net.simulateLatency = arg; }

    @serialize private _latency: time.sec = 1;
    @group("Net", "模拟延迟时长", showLatency, [0, 5, 0.1])
    public get latency(): time.sec { return this._latency; }
    public set latency(arg: time.sec) { this._latency = arg; if (!EDITOR) net.latency = arg; }

    @serialize private _logReq: boolean = false;
    @group("Net", "输出请求日志")
    public get logReq(): boolean { return this._logReq; }
    public set logReq(arg: boolean) { this._logReq = arg; if (!EDITOR) net.logReq = arg; }

    @serialize private _logResp: boolean = false;
    @group("Net", "输出返回日志")
    public get logResp(): boolean { return this._logResp; }
    public set logResp(arg: boolean) { this._logResp = arg; if (!EDITOR) net.logResp = arg; }

    @serialize private _useReport: boolean = false;
    @group("Net", "数据上报开关")
    public get useReport(): boolean { return this._useReport; }
    public set useReport(arg: boolean) { this._useReport = arg; if (!EDITOR) net.useReport = arg; }

    @serialize private _domain: Domain = Domain.PROD;
    @group("Net", "请求域设置", ["type", Enum(Domain)])
    public get domain(): Domain { return this._domain; }
    public set domain(arg: Domain) { this._domain = arg; if (!EDITOR) net.domain = arg; }

    @group("Net", "无遮罩API", ["type", [CCString]])
    public uncovers: string[] = [];

    /** ----------------- Audio ------------------- **/

    @serialize private _bgmVolume: num.pct = 0.5;
    @group("Aud", "背景音乐音量", [0, 1, 0.01])
    public get bgmVolume(): num.pct { return this._bgmVolume; }
    public set bgmVolume(arg: num.pct) { this._bgmVolume = arg; if (!EDITOR) aud.bgmVolume = arg; }

    @serialize private _bgmLoop: boolean = true;
    @group("Aud", "是否循环播放")
    public get bgmLoop(): boolean { return this._bgmLoop; }
    public set bgmLoop(arg: boolean) { this._bgmLoop = arg; if (!EDITOR) aud.bgmLoop = arg; }

    @serialize private _bgmPlayOnAwake: boolean = true;
    @group("Aud", "是否自动播放")
    public get bgmPlayOnAwake(): boolean { return this._bgmPlayOnAwake; }
    public set bgmPlayOnAwake(arg: boolean) { this._bgmPlayOnAwake = arg; if (!EDITOR) aud.bgmPlayOnAwake = arg; }

    @serialize private _sfxVolume: num.pct = 1;
    @group("Aud", "音效音量", [0, 1, 0.01])
    public get sfxVolume(): num.pct { return this._sfxVolume; }
    public set sfxVolume(arg: num.pct) { this._sfxVolume = arg; if (!EDITOR) aud.sfxVolume = arg; }

    @group("Aud", "启用默认音效") public useDefaultSfxs: boolean = false;

    @serialize private _sfxButton: AudioClip = null;
    @group("Aud", "默认按钮音效", AudioClip, showDefaultSfx)
    public get sfxButton(): AudioClip { return this._sfxButton; }
    public set sfxButton(arg: AudioClip) { this._sfxButton = arg; if (!EDITOR) aud.sfxButton = arg; }

    @serialize private _sfxToggle: AudioClip = null;
    @group("Aud", "默认切换音效", AudioClip, showDefaultSfx)
    public get sfxToggle(): AudioClip { return this._sfxToggle; }
    public set sfxToggle(arg: AudioClip) { this._sfxToggle = arg; if (!EDITOR) aud.sfxToggle = arg; }

    /** ----------------- Res ------------------- **/
    @serialize private _baseUrl: str.url = "" as str.url;
    @group("Res", "远程基址")
    public get remoteBaseUrl(): str.url { return this._baseUrl; }
    public set remoteBaseUrl(arg: str.url) { this._baseUrl = arg; if (!EDITOR) res.setRemoteBaseUrl(arg); }

    @serialize private _bundle: string = "";
    @group("Res", "远程包名")
    public get remoteBundleName(): string { return this._bundle; }
    public set remoteBundleName(arg: string) { this._bundle = arg; if (!EDITOR) res.setRemoteBundleName(arg); }

    protected onLoad(): void {
        director.addPersistRootNode(this.node);
        if (this._fullScreen && !screen.fullScreen()) this.fullScreen = true;
        this.showStats = this._showStats;
        if (!EDITOR) {
            aud.init(this.addComponent(AudioSource), this.addComponent(AudioSource));
            assign<NetManager, Root>(net, this, false, ...NetKeys);
            net.uncovers.push(...this.uncovers);
            assign<AudioManager, Root>(aud, this, false, ...AudKeys);
            if (this._baseUrl) res.setRemoteBaseUrl(this._baseUrl);
            if (this._bundle) res.setRemoteBundleName(this._bundle);
        }
    }

    protected onEnable(): void {
        // this.readjustCamera();
    }

    // 修正非Canvas下的2DCamera，TODO 含有targetRender的逻辑分支未修正
    public readjustCamera(): void {
        const alignCamera = this.getComponent(Canvas).cameraComponent;
        const layerCameras = this.getComponentsInChildren(Camera).remove(alignCamera);
        if (notEmptyArr(layerCameras)) {
            const height = screen.windowSize.height / view.getScaleY() / 2;
            const { x, y } = this.node.getWorldPosition();
            layerCameras.forEach(cam => {
                cam.orthoHeight = height;
                cam.node.setWorldPosition(x, y, 1000);
            });
        }
    }

    public toggleFullScreen(): void {
        this.fullScreen = !this.fullScreen;
    }

    public toggleShowStats(): void {
        this.showStats = !this.showStats;
    }
}